package com.imohoo.libs.retrofit2.ssl;

import okhttp3.internal.tls.OkHostnameVerifier;
import okio.Buffer;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;

/**
 * Created by zhaobo on 2016/8/10.
 */
public class CertUtils {
    /**
     * 证书
     */
    private static final String CERT = "-----BEGIN CERTIFICATE-----\n" +
            "MIIFCDCCA/CgAwIBAgISESGYypFx+6O9iX0Bxl+IIL6fMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV\n" +
            "BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERv\n" +
            "bWFpbiBWYWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwHhcNMTYwNTA1MDczODU4WhcNMTcwNTA2\n" +
            "MDczODU4WjA9MSEwHwYDVQQLExhEb21haW4gQ29udHJvbCBWYWxpZGF0ZWQxGDAWBgNVBAMMDyou\n" +
            "bXlydW5uZXJzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMpStXbG+CWwf2ZK\n" +
            "XFZi1JN2F+2SndY50Pymavgwx2wPnY2QeeIPMauPl+KjW7XuDuDsg3VJQD0gXeMuGfRIW/8CvVEv\n" +
            "M38RWGDEhNNJM/tVhM4jmYrW8427C/Kqh2+FvzFUVnMIY1IbHsnz98uzfqYmcof1om/p84YusGdq\n" +
            "9jdAs3NoJnSU5M5yC6Usl8tpnmWxNZHNw8/o+mtBVz8hGibSykNLgka9dIHnhJK/2Ay5xWitrzcd\n" +
            "Xe3p74bPfPcJDSG83rwwcLDln19Hh/4OIJaMRvACNcohQBhhAlSI2Si+34uV1cusqR26nBcFkRza\n" +
            "r0/qYI4xQCHjJFsFMw/cZ1ECAwEAAaOCAd0wggHZMA4GA1UdDwEB/wQEAwIFoDBWBgNVHSAETzBN\n" +
            "MEEGCSsGAQQBoDIBCjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9y\n" +
            "ZXBvc2l0b3J5LzAIBgZngQwBAgEwKQYDVR0RBCIwIIIPKi5teXJ1bm5lcnMuY29tgg1teXJ1bm5l\n" +
            "cnMuY29tMAkGA1UdEwQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEMGA1UdHwQ8\n" +
            "MDowOKA2oDSGMmh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nkb21haW52YWxzaGEyZzIu\n" +
            "Y3JsMIGUBggrBgEFBQcBAQSBhzCBhDBHBggrBgEFBQcwAoY7aHR0cDovL3NlY3VyZS5nbG9iYWxz\n" +
            "aWduLmNvbS9jYWNlcnQvZ3Nkb21haW52YWxzaGEyZzJyMS5jcnQwOQYIKwYBBQUHMAGGLWh0dHA6\n" +
            "Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9nc2RvbWFpbnZhbHNoYTJnMjAdBgNVHQ4EFgQUYDE9J4Wl\n" +
            "S4Bp21GR7HskvfVo2mwwHwYDVR0jBBgwFoAU6k581IAt5RWBhiaMgm3AmKTPlw8wDQYJKoZIhvcN\n" +
            "AQELBQADggEBAKnNFlOIn5cnHsZIdBpADwK6OcF61iVHhR/o6v0WrWDo+Rrb3LLvj8FApHtW15eI\n" +
            "qjtGO0eQOqsMb0lGMa+R/6fBp2L8U6sdCaWYXtWt10G7HQPJyDV26gcqBkLA43r9G6dYgitFu89d\n" +
            "c3kCi9XygNgupUf14hYTNLteqtS3QPP+3CPrgmoe34Zzxsox4DlhZUCFeSS29HfnIeKGwY3+wzDt\n" +
            "ilruyJLwda9X1igk2BBilsAE3ghYpn7ifXyDt3eaxu+6JBLR0QVHW2241AotP79dEBLEVMUx592E\n" +
            "tJGDK+Md3YIzaz1pGQUT1N80ynDPSrOnGFodWKZ3JS+baTYEf50=\n" +
            "-----END CERTIFICATE-----";
    /**
     * 证书中的公钥
     */
    private static final String PUB_KEY = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100ca52b576c6f825b07f664a5c5662d4937617ed929dd639d0fca66af830c76c0f9d8d9079e20f31ab8f97e2a35bb5ee0ee0ec837549403d205de32e19f4485bff02bd512f337f115860c484d34933fb5584ce23998ad6f38dbb0bf2aa876f85bf315456730863521b1ec9f3f7cbb37ea6267287f5a26fe9f3862eb0676af63740b37368267494e4ce720ba52c97cb699e65b13591cdc3cfe8fa6b41573f211a26d2ca434b8246bd7481e78492bfd80cb9c568adaf371d5dede9ef86cf7cf7090d21bcdebc3070b0e59f5f4787fe0e20968c46f00235ca21401861025488d928bedf8b95d5cbaca91dba9c1705911cdaaf4fea608e314021e3245b05330fdc67510203010001";

    /**
     * 证书锁定
     *
     * @return
     */
    public static SSLSocketFactory getSafeFromServer() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        // 以X.509方式获取证书
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        InputStream certInputStream = new Buffer().writeUtf8(CERT).inputStream();
        Certificate certificate = certificateFactory.generateCertificate(certInputStream);
        certInputStream.close();
        //System.out.println("cert key = " + certificate.getPublicKey().toString());

        // 生成一个包含服务器端证书的keystore
        String keyStoreType = KeyStore.getDefaultType();
        //System.out.println("key store type = " + keyStoreType);
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("0", certificate);

        // 用包含服务器端证书的keystore生成一个TrustManager
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        //System.out.println(tmfAlgorithm);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
        trustManagerFactory.init(keyStore);

        // 生成一个使用我们的TrustManager的SSLContext
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
        return sslContext.getSocketFactory();
    }

    public static SSLSocketFactory getSocketFactory(TrustManagerFactory trustManagerFactory) throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
        return sslContext.getSocketFactory();
    }

    public static TrustManagerFactory getTrustManagerFactory() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException {
        // 以X.509方式获取证书
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        InputStream certInputStream = new Buffer().writeUtf8(CERT).inputStream();
        Certificate certificate = certificateFactory.generateCertificate(certInputStream);
        certInputStream.close();
        //System.out.println("cert key = " + certificate.getPublicKey().toString());

        // 生成一个包含服务器端证书的keystore
        String keyStoreType = KeyStore.getDefaultType();
        //System.out.println("key store type = " + keyStoreType);
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("0", certificate);

        // 用包含服务器端证书的keystore生成一个TrustManager
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        //System.out.println(tmfAlgorithm);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
        trustManagerFactory.init(keyStore);
        return trustManagerFactory;
    }

    public static X509TrustManager getTrustManager(TrustManagerFactory trustManagerFactory) {
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        if (trustManagers.length < 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
        }
        return (X509TrustManager) trustManagers[0];
    }


    /**
     * 自定义校验服务端证书
     *
     * @return
     */
    public static TrustManager[] getTrustManagers() {
        // Create a trust manager that does not validate certificate chains
        return new TrustManager[]{new X509TrustManager() {

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            //客户端并为对ssl证书的有效性进行校验
            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
                if (x509Certificates == null) {
                    throw new IllegalArgumentException("checkServerTrusted: x509Certificates is null");
                }

                if (!(x509Certificates.length > 0)) {
                    throw new IllegalArgumentException("checkServerTrusted: X509Certificates is empty");
                }

                if (!(null != authType && authType.toUpperCase().contains("RSA"))) {
                    throw new CertificateException("checkServerTrusted: AuthType is not RSA");
                }
                // Perform customary SSL/TLS checks
                // 第一种验证方式
//                try {
//                    TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
//                    tmf.init(keyStore);
//                    for (TrustManager trustManager : tmf.getTrustManagers()) {
//                        ((X509TrustManager) trustManager).checkServerTrusted(x509Certificates, authType);
//                    }
//                } catch (Exception e) {
//                    throw new CertificateException(e);
//                }
                // 判断错误
                //    try {
                //        for (X509Certificate cert : x509Certificates) {
                //            cert.checkValidity();
                //            cert.verify(getCert().getPublicKey());
                //        }
                //    } catch (Exception ex) {
                //        ex.printStackTrace();
                //    }
                // 第二种验证方式
                // 生成比较密钥对
                // 算法有点问题
//                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
//                InputStream certificate = new Buffer().writeUtf8(TestHttp.cer).inputStream();
//                Certificate c = certificateFactory.generateCertificate(certificate);
//                String result = new BigInteger(1 /* positive */, c.getEncoded()).toString(16);

                // Hack ahead: BigInteger and toString(). We know a DER encoded Public Key begins
                // with 0×30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is no leading 0×00 to drop.
                RSAPublicKey pubkey = (RSAPublicKey) x509Certificates[0].getPublicKey();

                String encoded = new BigInteger(1 /* positive */, pubkey.getEncoded()).toString(16);
                // Pin it!
                final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);

                if (!expected) {
                    throw new CertificateException("checkServerTrusted: Expected public key: " + PUB_KEY + ", got public key:" + encoded);
                }
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[0];
            }
        }};
    }


    /**
     * 校验服务器证书域名是否相符
     *
     * @param hostUrls
     * @return
     */
    public static HostnameVerifier getHostnameVerifier(final String[] hostUrls) {
        return new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                HostnameVerifier hostnameVerifier = OkHostnameVerifier.INSTANCE;
                boolean ret = false;
                for (String host : hostUrls) {
                    if (hostnameVerifier.verify(host, session)) {
                        ret = true;
                        break;
                    }
                }
                return ret;
            }
        };
    }
}
